#!/usr/bin/env php
<?php
/**
 *
 * [WeEngine System] Copyright (c) 2014 W7.CC
 */

ini_set('display_errors', 1);
error_reporting(E_ALL ^ E_NOTICE);
set_time_limit(0);
@ini_set('memory_limit', '1356M');

if (strtoupper(php_sapi_name()) != 'CLI') {
	We7Command::line('只能在命令行执行');
}

set_exception_handler(function (Error $ex) {
	echo $ex->getTraceAsString();
	echo $ex->getMessage();
	return true;
});
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
	echo $errno . ':' . $errstr . 'file:' . $errfile . ':line' . $errline . PHP_EOL;
	return true;
});

if (!function_exists('pdoQuery')) {
	function pdoQuery() {
		load()->classs('query');
		return new Query();
	}
}
if (! function_exists('env')) {
	function env($name, $default = null) {
		$value = getenv($name);
		if (! $value) {
			return $default;
		}
		return $value;
	}
}

$_W = array();
$path = dirname(__FILE__);
chdir($path);

We7Command::execute();

abstract class We7Command {
	protected $name;
	protected $description;

	public function __construct() {
		$this->init();
	}

	/**
	 *  安装脚本不能加载此文件 因config.php 文件不存在
	 */
	protected function init() {
		global $_W;
		include_once $this->currentPath() . '/framework/bootstrap.inc.php';
	}

	public static function execute() {
		/** @var We7Command $command */
		$command = self::createCommand();
		if ($command) {
			$command->handle();
			return;
		}
		self::line('当前命令不存在');
		self::line('支持的命令如下:');
		//php console.php upgrade
		self::line('php console.php upgrade => 执行更新 ');
		//php console.php install host=172.0.0.1 port=3306 dbName=w7_pros_test userName=root password=123456
		self::line('php console.php install => 执行安装微擎脚本');
		//php console.php make:upgrade name=update_modules
		self::line('php console.php make:upgrade name=更新的文件名 => 创建更新文件');
		//php console.php make:config host=172.0.0.1 port=3306 dbName=w7_pros_test userName=root password=123456
		self::line('php console.php make:config => 生成系统配置文件config.php');
		//php console.php init:database remote_attachment=w7://qiniu/JjpqIPjIb9alteX5hlr1Xs5F_ywcFk0VvDz54hA_/qJBYPh-nZfm347tcjn5Mo3JK2LejtkcllyWigSOP/ccceshi?http://ptfzt9nhb.bkt.clouddn.com
		self::line('php console.php init:database => 初始化数据库脚本');
		//php console.php change:founder username=admin password=159951
		self::line('php console.php change:founder => 修改创始人账号密码');
		//php console.php reset:site site_url=http://prox.w7.com
		self::line('php console.php reset:site site_url=授权域名 => 重置站点信息');
		//php console.php module:install module_name=w7_demo_test
		self::line('php console.php module:install module_name=安装的应用标识 => 执行安装应用');
		//php console.php module:upgrade module_name=w7_demo_test
		self::line('php console.php module:upgrade module_name=更新的应用标识 => 执行更新应用');
		//php console.php module:uninstall module_name=w7_demo_test
		self::line('php console.php module:uninstall module_name=卸载的应用标识 => 执行卸载应用');
	}

	public static function createCommand() {
		$argv = $_SERVER['argv'];
		$argc = $_SERVER['argc'];

		if ($argc > 1) {
			$commandName = $argv[1];
			if ($commandName == 'make:upgrade') {
				return new We7CreateUpgradeCommand();
			}
			if ($commandName == 'upgrade') {
				return new We7UpgradeCommand();
			}
			if ($commandName == 'install') {
				return new We7InstallCommand();
			}
			if ($commandName == 'make:config') {
				return new We7MakeConfigCommand();
			}
			if ($commandName == 'init:database') {
				return new We7InitDatabaseCommand();
			}
			if ($commandName == 'change:founder') {
				return new We7ChangeFounderCommand();
			}
			if ($commandName == 'reset:site') {
				return new We7ResetSiteCommand();
			}
			if ($commandName == 'module:install') {
				return new We7ModuleInstallCommand();
			}
			if ($commandName == 'module:upgrade') {
				return new We7ModuleUpgradeCommand();
			}
			if ($commandName == 'module:uninstall') {
				return new We7ModuleUninstallCommand();
			}
		}

		return null;
	}

	abstract public function handle();

	/**
	 * @param string|int $name
	 *
	 * @return bool
	 */
	public function hasArgument($name) {
		return !is_null($this->argument($name));
	}

	/**
	 * Get the value of a command argument.
	 *
	 * @param string $key
	 *
	 * @return string|array
	 */
	public function argument($key) {
		$arguments = $this->arguments();
		if (isset($arguments[$key])) {
			return $arguments[$key];
		}

		return null;
	}

	/**
	 * Get all of the arguments passed to the command.
	 *
	 * @return array
	 */
	public function arguments() {
		$argv = $_SERVER['argv'];
		$argc = $_SERVER['argc'];
		$result = array();
		if ($argc >= 2) {
			$args = array_slice($argv, 2);
			array_map(function ($item) use (&$result) {
				list($key, $value) = explode('=', $item);
				$result[$key] = $value;
			}, $args);

			return $result;
		}

		return $result;
	}

	public static function line($string, $eol = true) {
		if (strtoupper(PHP_OS) != 'LINUX') {
			//$string = iconv('UTF-8', 'GBK', $string);
		}
		$string = $eol ? $string . PHP_EOL : $string;
		fwrite(STDOUT, $string . PHP_EOL);
	}

	/**
	 *  确定是否更新.
	 *
	 * @param string $question
	 * @param bool   $default
	 *
	 * @return bool
	 */
	public function confirm($question, $yes = 'Y') {
		$this->line($question . '(Y/N)');
		$value = fscanf(STDIN, '%s');
		if (strtoupper($value[0]) == 'Y') {
			return true;
		}

		return false;
	}

	public function input($question, $default = null) {
		$defaults = is_array($default) ? implode($default, '|') : $default;
		$this->line("$question($defaults) :", false);
		$value = fscanf(STDIN, '%s');
		$value = $value[0];
		if (empty($value)) {
			return $default;
		}
		if (is_array($default) && !in_array($value, $default)) {
			return null;
		}
		return $value;
	}

	public function currentPath() {
		return getcwd();
	}

	protected function debug($value) {
		$this->line(is_array($value) ? var_export($value, true) : $value);
	}

	public function random($length) {
		$strs = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklnmopqrstuvwxyz0123456789';
		$result = substr(str_shuffle($strs), mt_rand(0, strlen($strs) - ($length + 1)), $length);
		return $result;
	}
}

class We7CreateUpgradeCommand extends We7Command {
	protected $name = 'make:upgrade';

	protected function init() {
		parent::init(); // TODO: Change the autogenerated stub
	}

	public function handle() {
		$name = $this->argument('name');
		$path = $this->getPath();
		$prefix = date('Y_m_d_His');
		@mkdir($path, 0777, true);
		$filepath = $path . '/' . $prefix . '_' . $name . '.php';
		echo $filepath;
		file_put_contents($filepath, $this->template($name));
	}

	private function getPath() {
		$dir = $this->getDir();

		return 'upgrade' . DIRECTORY_SEPARATOR . $dir;
	}

	private function getDir() {
		return '' . $this->getVersion();
	}

	private function getVersion() {
		return IMS_VERSION;
	}

	private function toClassName($name) {
		$value = ucwords(str_replace(array('-', '_'), ' ', $name));

		return str_replace(' ', '', $value);
	}

	private function template($name) {
		$time = time();
		$version = $this->getVersion();
		$namespace = 'We7\V' . str_replace('.', '', $version);
		$name = $this->toClassName($name);
		$template = <<<EOT
<?php

namespace $namespace;

defined('IN_IA') or exit('Access Denied');
/**
* [WeEngine System] Copyright (c) 2014 W7.CC
* Time: $time
* @version $version
*/

class $name {

/**
 *  执行更新
 */
public function up() {

}

/**
 *  回滚更新
 */
public function down() {


}
}

EOT;

		return $template;
	}
}

class We7UpgradeCommand extends We7Command {
	protected $name = 'upgrade';
	private $project_upgrade_files = array(); // upgrade 目录下的所有文件

	protected function init() {
		parent::init();
	}
	/**
	 *  处理更新脚本.
	 */
	public function handle() {
		$filename = $this->argument('filename');
		$version = $this->argument('version');
		// 强制执行单个文件更新
		if (!empty($filename) && !empty($version)) {
			$this->update_single_file($filename, $version);

			return;
		}
		//执行全部更新
		$this->update();
	}

	private function update_single_file($filename, $version) {
		$filepath = 'upgrade' . DIRECTORY_SEPARATOR . $version . DIRECTORY_SEPARATOR . $filename . '.php';
		$include = include_once $filepath;
		if (!$include) {
			self::line($filepath . '文件未找到');

			return;
		}
		$batch = $this->get_max_batch();
		$this->update_single($filename, $version, $batch);

		return;
	}

	public function update() {
		$this->check_table();
		$files = $this->diff_files();
		echo 'filecount：' . count($files) . PHP_EOL;
		if (count($files) == 0) {
			$this->line('没有要更新的文件');

			return;
		}
		echo 'confirm' . PHP_EOL;
		if ($this->confirm('确认更新吗?')) {
			echo 'doupdate：' . PHP_EOL;
			$this->doUpgrade($files);
		}
	}

	/**
	 * 执行 更新.
	 */
	private function doUpgrade($files) {
		$batch = $this->get_max_batch();
		foreach ($files as $filename => $version) {
			$this->update_single($filename, $version, $batch);
		}
	}

	private function get_max_batch() {
		$batch = pdo_fetch('SELECT MAX(batch) as batch FROM ' . tablename('upgrade'));
		$batch = $batch['batch'] + 1;

		return $batch;
	}

	private function update_single($filename, $version, $batch) {
		$include_path = 'upgrade/' . $version . '/' . $filename . '.php';
		include_once $include_path;
		$class = $this->resolve($filename, $version);
		$ignore = 'ignore';
		if (property_exists($class, $ignore)) {
			return; //如果有ignore属性 直接略过 (云服务更新可能不需要这个更新文件)
		}
		$description = 'description';
		$des_exits = property_exists($class, $description);
		$upinfo = '更新 ' . $version . ' : ' . $filename . '.php';
		if ($des_exits) {
			$upinfo .= ':' . $class->{$description};
		}
		$this->line($upinfo);
		if (method_exists($class, 'up')) {
			call_user_func(array($class, 'up'));
		}
		pdo_insert('upgrade', array('file' => $filename, 'batch' => $batch, 'version' => $version, 'createtime' => time()));
	}

	public function resolve($file, $version) {
		$class = $this->studly(implode('_', array_slice(explode('_', $file), 4)));
		$namespace = '\\We7\\V' . str_replace('.', '', $version);
		$class = $namespace . '\\' . $class;

		return new $class();
	}

	/**
	 *  名称转class 名.
	 *
	 * @param $name
	 *
	 * @return string
	 */
	private function studly($name) {
		$value = ucwords(str_replace(array('-', '_'), ' ', $name));

		return str_replace(' ', '', $value);
	}

	/**
	 *  获取diff 文件.
	 *
	 * @return array
	 */
	private function diff_files() {
		$dbfiles = pdoQuery()->from('upgrade')->getall('file'); //获取数据库目录
		$dbfiles = array_keys($dbfiles); //数据库文件
		$this->project_upgrade_files = $this->get_project_upgrade_files();
		$files = array_keys($this->project_upgrade_files);
		$diff_files = array_diff_key($this->project_upgrade_files, array_flip($dbfiles)); // 比对差异文件
		return $diff_files;
	}

	/**
	 *  检查表是否存在.
	 */
	private function check_table() {
		$exits = pdo_tableexists('upgrade');
		if (!$exits) {
			$isCreate = pdo_query("CREATE TABLE IF NOT EXISTS `ims_upgrade` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`file` varchar(255) NOT NULL COMMENT 'upgrade 目录名',
`createtime` int(11) NOT NULL COMMENT '创建时间',
`batch` int(11) NOT NULL COMMENT '批次',
`version` varchar(255) NOT NULL DEFAULT '' COMMENT '版本号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; ");
		}
	}

	/**
	 *  获取upgrade 下的所有文件
	 * return [filename=>version].
	 *
	 * @return array
	 */
	private function get_project_upgrade_files() {
		$directory = new \RecursiveDirectoryIterator('./upgrade');
		$iterator = new \RecursiveIteratorIterator($directory);
		$files = array();
		$system_version = IMS_VERSION;
		foreach ($iterator as $file) {
			if ('php' == $file->getExtension()) {
				$path = $file->getPath();
				$version = pathinfo($path, PATHINFO_BASENAME);
				$basename = $file->getBasename('.php');
				if (version_compare($system_version, $version, '>')) {
					continue;
				}
				if ($basename != 'upgrade' && $basename != 'upgrade_0.x_to_1.0') {
					$files[$basename] = $version;
				}
			}
		}

		return $files;
	}
}

class We7InstallCommand extends We7Command {
	protected $name = 'install';
	protected $table_pre = 'ims_';

	protected $host;
	protected $port;
	protected $dbName;
	protected $userName;
	protected $password;
	protected $w7password;
	protected $authKey;

	protected function init() {
		//parent::init();
	}

	public function handle() {
		if (file_exists($this->rootPath() . '/data/config.php')) {
			$this->line('w7已配置');
			return;
		}
		$this->host = $this->argument('host');
		$this->port = $this->argument('port');
		$this->dbName = $this->argument('dbName');
		$this->userName = $this->argument('userName');
		$this->password = $this->argument('password');
		$this->w7password = $this->argument('w7password') ?? '123456';
		$this->cookiepre = $this->random(4) . '_';
		$this->authKey = $this->random(15);

		gc_disable();
		if (extension_loaded('pdo') && !extension_loaded('pdo_mysql')) {
			$this->line('安装出错 请确保pdo 和 pdo_mysql扩展已开启');
			return;
		}
		ini_set('memory_limit', '1024M');

		$this->dbconfig();
		$this->installDB();
		$this->createConfig();

		$this->addAdmin();
		$this->updateTable();
		$this->line('微擎数据安装成功 默认用户名 admin 密码 ' . $this->w7password);
	}

	private function host() {
		if ($this->host) {
			return $this->host;
		}
		$result = $this->input("请输入有效的数据库域名或ip<please enter db host or ip>", '127.0.0.1');
		if (!(filter_var($result, FILTER_VALIDATE_IP) || filter_var($result, FILTER_VALIDATE_URL) || $result == 'localhost')) {
			return $this->host();
		}
		return $result;
	}

	private function port() {
		if ($this->port) {
			return $this->port;
		}
		$result = $this->input('请输入数据库端口(please enter db port)', 3306);
		$port = intval($result);
		if ($port <= 1024) {
			$this->line("请输入有效的数据库端口<please enter valid port>", false);
			return $this->port();
		}
		return $port;
	}

	private function dbName() {
		if ($this->dbName) {
			return $this->dbName;
		}
		$result = $this->input("请输入数据库名称<please enter valid db name>", 'we7');
		if (empty($result)) {
			return $this->dbName();
		}
		return $result;
	}

	private function dbUserName() {
		if ($this->userName) {
			return $this->userName;
		}
		$result = $this->input('请输入数据库用户名<please enter valid db username>', 'root');
		if (empty($result)) {
			return $this->dbUserName();
		}
		return $result;
	}

	private function dbPassword() {
		if ($this->password) {
			return $this->password;
		}
		$result = $this->input('请输入数据库密码<please enter valid db password>', '');
		return $result;
	}

	private function dbconfig() {
		$this->host = $this->host();
		$this->port = $this->port();
		$this->dbName = $this->dbName();
		$this->userName = $this->dbUserName();
		$this->password = $this->dbPassword();
	}

	private function createDB($dbName) {
		$dsn = "mysql:dbName=;host={$this->host};port={$this->port};charset=utf8";
		$pdo = new PDO($dsn, $this->userName, $this->password);
		$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		$pdo->exec('create database ' . $dbName);
		return $pdo;
	}
	/**
	 * config 文件未写入 pdo 内置方法暂不可用
	 */
	private function createPDO($useDbName = true) {
		$dbName = $useDbName ? $this->dbName : '';
		$dsn = "mysql:dbname={$dbName};host={$this->host};port={$this->port};charset=utf8";
		$pdo = new PDO($dsn, $this->userName, $this->password);
		$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		return $pdo;
	}
	/**
	 *  检查数据库连接
	 */
	private function checkConnection() {
		try {
			$pdo = $this->createPDO();
			$sql = "SET NAMES 'utf8';";
			$pdo->exec($sql);
		} catch (PDOException $e) {
			if ($e->getCode() == 1045) {
				$this->line('数据库连接错误, 请关闭终端重新执行此命令' . $e->getCode() . ':' . $e->getMessage());
				return false;
			}
		}
		return true;
	}

	protected function exec($sql, $throw = true) {
		try {
			$pdo = $this->createPDO();
			$default = "SET NAMES 'utf8';";
			$pdo->exec($default);
			$pdo->exec($sql);
		} catch (PDOException $e) {
			if ($throw) {
				$this->line($e->getMessage());
				throw $e;
			}
		}
		return true;
	}

	/**
	 * 检查数据库是否存在
	 * @return bool
	 */
	private function checkDBName() {
		try {
			$pdo = $this->createPDO(false);
			$sql = "SELECT * FROM information_schema.SCHEMATA where SCHEMA_NAME='{$this->dbName}'";
			$result = $pdo->query($sql)->fetch();
			if (!empty($result)) {
				$this->line('数据库已存在,请重新指定数据库', false);
				$this->dbName = $this->dbName();
				return $this->checkDBName();
			}
		} catch (PDOException $e) {
			if ($e->getCode() == 1045) {
				$this->line('数据库连接错误, 请关闭终端重新执行此命令' . $e->getCode() . ':' . $e->getMessage());
				return false;
			}
			$this->line('dbname' . $e->getMessage());
			return false;
		}

		try {
			$this->debug('create database');
			$this->createDB($this->dbName);
		} catch (Exception $e) {
			$this->line('创建数据库失败:' . $e->getMessage());
			return false;
		}

		return true;
	}

	/**
	 *   创建ip 端口
	 */
	private function createConfig() {
		$config = <<<EOT

<?php
defined('IN_IA') or exit('Access Denied');

\$config = array();

\$config['db']['master']['host'] = '$this->host';
\$config['db']['master']['username'] = '$this->userName';
\$config['db']['master']['password'] = '$this->password';
\$config['db']['master']['port'] = '$this->port';
\$config['db']['master']['database'] = '$this->dbName';
\$config['db']['master']['charset'] = 'utf8';
\$config['db']['master']['pconnect'] = 0;
\$config['db']['master']['tablepre'] = 'ims_';

\$config['db']['slave_status'] = false;
\$config['db']['slave']['1']['host'] = '';
\$config['db']['slave']['1']['username'] = '';
\$config['db']['slave']['1']['password'] = '';
\$config['db']['slave']['1']['port'] = '3307';
\$config['db']['slave']['1']['database'] = '';
\$config['db']['slave']['1']['charset'] = 'utf8';
\$config['db']['slave']['1']['pconnect'] = 0;
\$config['db']['slave']['1']['tablepre'] = 'ims_';
\$config['db']['slave']['1']['weight'] = 0;

\$config['db']['common']['slave_except_table'] = array('core_sessions');

// --------------------------  CONFIG COOKIE  --------------------------- //
\$config['cookie']['pre'] = '$this->cookiepre';
\$config['cookie']['domain'] = '';
\$config['cookie']['path'] = '/';

// --------------------------  CONFIG SETTING  --------------------------- //
\$config['setting']['charset'] = 'utf-8';
\$config['setting']['cache'] = 'mysql';
\$config['setting']['timezone'] = 'Asia/Shanghai';
\$config['setting']['memory_limit'] = '256M';
\$config['setting']['filemode'] = 0644;
\$config['setting']['authkey'] = '$this->authKey';
\$config['setting']['founder'] = '1';
\$config['setting']['development'] = 0;
\$config['setting']['referrer'] = 0;

// --------------------------  CONFIG UPLOAD  --------------------------- //
\$config['upload']['image']['extentions'] = array('gif', 'jpg', 'jpeg', 'png');
\$config['upload']['image']['limit'] = 5000;
\$config['upload']['attachdir'] = 'attachment';
\$config['upload']['audio']['extentions'] = array('mp3');
\$config['upload']['audio']['limit'] = 5000;

// --------------------------  CONFIG MEMCACHE  --------------------------- //
\$config['setting']['memcache']['server'] = '';
\$config['setting']['memcache']['port'] = 11211;
\$config['setting']['memcache']['pconnect'] = 1;
\$config['setting']['memcache']['timeout'] = 30;
\$config['setting']['memcache']['session'] = 1;


// --------------------------  CONFIG PROXY  --------------------------- //
\$config['setting']['proxy']['host'] = '';
\$config['setting']['proxy']['auth'] = '';

EOT;
		return file_put_contents($this->rootPath() . '/data/config.php', $config);
	}

	private function rootPath() {
		return getcwd();
	}

	/**
	 *  获取数据库建表语句和插入数据sql
	 */
	private function dbSql() {
		$path = $this->rootPath() . '/data/db-1.x.php';
		if (!file_exists($path)) {
			$this->line('数据库sql 脚本不存在, 请确保' . $path . '文件存在', false);
		}
		$dst = include_once $path;
		return $dst;
	}
	/**
	 *  执行安装数据库
	 */
	protected function installDB() {
		$connect = $this->checkConnection();
		if (!$connect) {
			return;
		}
		if (!$this->checkDBName()) {
			return ;
		}

		if (!$scheme_sql = $this->dbSql()) {
			return;
		}
		foreach ($scheme_sql['schemas'] as $schema) {
			$this->line('新建:' . $schema['tablename'] . '表', false);
			$sql = $this->local_create_sql($schema);
			try {
				$this->exec($sql);
			} catch (Exception $e) {
				$this->debug('debug start');
				$this->debug($sql);
				$this->debug('debug end');
				break;
			}
		}

		foreach ($scheme_sql['datas'] as $sql) {
			try {
				$this->exec($sql);
			} catch (Exception $e) {
				$this->debug('debug start');
				$this->debug($sql);
				$this->debug('debug end');
			}
		}
	}

	protected function randomStr($length = 16) {
		$string = '';

		while (($len = strlen($string)) < $length) {
			$size = $length - $len;

			$bytes = random_bytes($size);

			$string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
		}

		return $string;
	}

	protected function addAdmin() {
		$tableName = $this->table_pre . 'users';
		$salt = $this->randomStr(8);
		$password = "{$this->w7password}-{$salt}-{$this->authKey}";
		$password = sha1($password);
		$sql = <<<EOT
     INSERT INTO $tableName(`uid`, `owner_uid`, `groupid`, `founder_groupid`, `username`, `password`, `salt`, `type`, `status`, `joindate`, `joinip`, `lastvisit`, `lastip`, `remark`, `starttime`, `endtime`, `register_type`, `openid`, `welcome_link`, `notice_setting`, `is_bind`) VALUES (1, 0, 0, 1, 'admin', '$password', '$salt', 2, 127, 1520388458, '1520388458', 444, '127.0.0.1', '中华人民共和国万岁和国万岁和国万岁和国万岁和国万岁和国万岁', 1456743079, 0, 1, '0', 0, 0, 0);
EOT;
		try {
			$this->exec($sql);
		} catch (Exception $e) {
			$this->line('插入默认用户出错,请手动执行本sql');
			$this->line('start sql');
			$this->line($sql);
			$this->line('end sql');
		}
	}

	private function updateTable() {
		$this->line('开始数据库表结构更新: start update table schama....');
		$command = new We7UpgradeCommand();
		$command->update();
		$this->line('开始数据库表结构更新完成: end update table schama');
	}

	/**
	 * schme 返回建表语句
	 * @param $schema
	 * @return string
	 */
	private function local_create_sql($schema) {
		$pieces = explode('_', $schema['charset']);
		$charset = $pieces[0];
		$engine = $schema['engine'];
		$sql = "CREATE TABLE IF NOT EXISTS `{$schema['tablename']}` (\n";
		foreach ($schema['fields'] as $value) {
			if (!empty($value['length'])) {
				$length = "({$value['length']})";
			} else {
				$length = '';
			}

			$signed = empty($value['signed']) ? ' unsigned' : '';
			if (empty($value['null'])) {
				$null = ' NOT NULL';
			} else {
				$null = '';
			}
			if (isset($value['default'])) {
				$default = " DEFAULT '" . $value['default'] . "'";
			} else {
				$default = '';
			}
			if ($value['increment']) {
				$increment = ' AUTO_INCREMENT';
			} else {
				$increment = '';
			}

			$sql .= "`{$value['name']}` {$value['type']}{$length}{$signed}{$null}{$default}{$increment},\n";
		}
		foreach ($schema['indexes'] as $value) {
			$fields = implode('`,`', $value['fields']);
			if ($value['type'] == 'index') {
				$sql .= "KEY `{$value['name']}` (`{$fields}`),\n";
			}
			if ($value['type'] == 'unique') {
				$sql .= "UNIQUE KEY `{$value['name']}` (`{$fields}`),\n";
			}
			if ($value['type'] == 'primary') {
				$sql .= "PRIMARY KEY (`{$fields}`),\n";
			}
		}
		$sql = rtrim($sql);
		$sql = rtrim($sql, ',');

		$sql .= "\n) ENGINE=$engine DEFAULT CHARSET=$charset;\n\n";
		return $sql;
	}
}

class We7MakeConfigCommand extends We7Command {
	protected $name = 'make:config';

	protected function init() {
	}

	public function handle() {
		if (file_exists($this->currentPath() . '/data/config.php')) {
			$this->line('config文件已配置');
			return true;
		}
		$this->completeConfig();
		$this->line('config.php生成成功!');
		return true;
	}

	private function initConfig() {
		$config = <<<EOT
<?php
defined('IN_IA') or exit('Access Denied');

\$config = array();

\$config['db']['master']['host'] = '{DB_HOST}';
\$config['db']['master']['username'] = '{DB_USERNAME}';
\$config['db']['master']['password'] = '{DB_PASSWORD}';
\$config['db']['master']['port'] = '{DB_PORT}';
\$config['db']['master']['database'] = '{DB_DATABASE}';
\$config['db']['master']['charset'] = 'utf8';
\$config['db']['master']['pconnect'] = 0;
\$config['db']['master']['tablepre'] = '{DB_TABLEPRE}';

\$config['db']['slave_status'] = false;
\$config['db']['slave']['1']['host'] = '';
\$config['db']['slave']['1']['username'] = '';
\$config['db']['slave']['1']['password'] = '';
\$config['db']['slave']['1']['port'] = '3307';
\$config['db']['slave']['1']['database'] = '';
\$config['db']['slave']['1']['charset'] = 'utf8';
\$config['db']['slave']['1']['pconnect'] = 0;
\$config['db']['slave']['1']['tablepre'] = 'ims_';
\$config['db']['slave']['1']['weight'] = 0;

\$config['db']['common']['slave_except_table'] = array('core_sessions');

// --------------------------  CONFIG COOKIE  --------------------------- //
\$config['cookie']['pre'] = '{COOKIEPRE}';
\$config['cookie']['domain'] = '';
\$config['cookie']['path'] = '/';

// --------------------------  CONFIG SETTING  --------------------------- //
\$config['setting']['charset'] = 'utf-8';
\$config['setting']['cache'] = '{CACHE}';
\$config['setting']['timezone'] = 'Asia/Shanghai';
\$config['setting']['memory_limit'] = '256M';
\$config['setting']['filemode'] = 0644;
\$config['setting']['authkey'] = '{AUTHKEY}';
\$config['setting']['founder'] = '1';
\$config['setting']['development'] = 1;
\$config['setting']['referrer'] = 0;

// --------------------------  CONFIG UPLOAD  --------------------------- //
\$config['upload']['image']['extentions'] = array('gif', 'jpg', 'jpeg', 'png');
\$config['upload']['image']['limit'] = 5000;
\$config['upload']['attachdir'] = 'attachment';
\$config['upload']['audio']['extentions'] = array('mp3');
\$config['upload']['audio']['limit'] = 5000;

// --------------------------  CONFIG MEMCACHE  --------------------------- //
\$config['setting']['memcache']['server'] = '{MEMCACHE_SERVER}';
\$config['setting']['memcache']['port'] = '{MEMCACHE_PORT}';
\$config['setting']['memcache']['pconnect'] = 1;
\$config['setting']['memcache']['timeout'] = 30;
\$config['setting']['memcache']['session'] = 1;

// --------------------------  CONFIG REDIS  --------------------------- //
\$config['setting']['redis']['server'] =  '{REDIS_SERVER}';
\$config['setting']['redis']['port'] = '{REDIS_PORT}';
\$config['setting']['redis']['pconnect'] = 1;
\$config['setting']['redis']['timeout'] = 30;
\$config['setting']['redis']['session'] = 1;
\$config['setting']['redis']['auth'] = '';

// --------------------------  CONFIG PROXY  --------------------------- //
\$config['setting']['proxy']['host'] = '';
\$config['setting']['proxy']['auth'] = '';
EOT;
		return $config;
	}
	private function completeConfig() {
		$this->host = getenv('DB_HOST') ?: $this->argument('host');
		$this->userName = getenv('DB_USERNAME') ?: $this->argument('userName');
		$this->password = getenv('DB_PASSWORD') ?: $this->argument('password');
		$this->port = getenv('DB_PORT') ?: $this->argument('port');
		$this->dbName = getenv('DB_DATABASE') ?: $this->argument('dbName');
		$this->table_pre = getenv('DB_TABLEPRE') ?: 'ims_';
		$this->cookiepre = getenv('COOKIEPRE') ?: ($this->random(4) . '_');
		$this->cache = getenv('CACHE') ?: 'mysql';
		$this->authKey = getenv('AUTHKEY') ?: ($this->random(16) . '_');
		$this->memcache_server = getenv('MEMCACHE_SERVER') ?: '';
		$this->memcache_port = getenv('MEMCACHE_PORT') ?: '';
		$this->redis_server = getenv('REDIS_SERVER') ?: '';
		$this->redis_port = getenv('REDIS_PORT') ?: '';

		$config = $this->initConfig();
		$config = str_replace(array(
			'{DB_HOST}', '{DB_USERNAME}', '{DB_PASSWORD}', '{DB_PORT}', '{DB_DATABASE}', '{DB_TABLEPRE}', '{COOKIEPRE}', '{CACHE}', '{AUTHKEY}', '{MEMCACHE_SERVER}', '{MEMCACHE_PORT}', '{REDIS_SERVER}', '{REDIS_PORT}'
		), array(
			$this->host, $this->userName, $this->password, $this->port, $this->dbName, $this->table_pre, $this->cookiepre, $this->cache, $this->authKey, $this->memcache_server, $this->memcache_port, $this->redis_server, $this->redis_port
		), $config);
		$result = file_put_contents($this->currentPath() . '/data/config.php', $config);
		return $result;
	}
}

class We7InitDatabaseCommand extends We7InstallCommand {
	protected $name = 'init:database';
	protected $config;

	protected function init() {
		global $_W;
		define('IN_IA', true);
		include_once $this->currentPath() . '/data/config.php';
		$this->config = $config;
	}

	public function handle() {
		gc_disable();
		if (extension_loaded('pdo') && !extension_loaded('pdo_mysql')) {
			$this->line('安装出错 请确保pdo 和 pdo_mysql扩展已开启');
			return;
		}
		ini_set('memory_limit', '1024M');

		$this->host = $this->config['db']['master']['host'];
		$this->userName = $this->config['db']['master']['username'];
		$this->password = $this->config['db']['master']['password'];
		$this->port = $this->config['db']['master']['port'];
		$this->dbName = $this->config['db']['master']['database'];
		$this->authKey = $this->config['setting']['authkey'];
		$this->w7password = getenv('W7_PASSWORD') ?: '123456';

		$this->installDB();
		$this->addAdmin();
		$this->saveRemoteAttachment();
		$this->line('微擎数据安装成功 默认用户名 admin 密码 ' . $this->w7password);
	}

	private function saveRemoteAttachment() {
		//形如：w7://qiniu/Accesskey/Secretkey/Bucket?Url
		//$remote_attachment = $this->argument('remote_attachment');
		$remote_attachment = getenv('REMOTE_ATTACHMENT');
		if (empty($remote_attachment)) {
			return true;
		}
		$remote_attachment = parse_url($remote_attachment);
		$remote_params = explode('/', $remote_attachment['path']);
		$remote_value = array(
			'type' => 3,
			'alioss' => array(),
			'ftp' => array(),
			'cos' => array(),
			'qiniu' => array(
				'accesskey' => $remote_params[1],
				'secretkey' => $remote_params[2],
				'bucket' => $remote_params[3],
				'url' => $remote_attachment['query'],
			)
		);
		$tableName = $this->table_pre . 'core_settings';
		$remote_value = serialize($remote_value);
		$sql = <<<EOT
     INSERT INTO $tableName (`key`, `value`) VALUES ('remote', '$remote_value');
EOT;
		try {
			$this->exec($sql);
		} catch (Exception $e) {
			$this->line('插入远程附件配置出错,请手动执行本sql');
			$this->line('start sql');
			$this->line($sql);
			$this->line('end sql');
		}
	}
}

class We7ChangeFounderCommand extends We7Command {
	public function handle() {
		$username = $this->argument('username');
		$password = $this->argument('password');
		load()->model('user');
		$userinfo = pdo_get('users', array('uid' => '1'));
		$password = user_hash($password, $userinfo['salt']);
		$result = pdo_update('users', array('username' => $username, 'password' => $password), array('uid' => $userinfo['uid']));
		if ($result) {
			$this->line('创始人用户名密码更改成功！');
		} else {
			$this->line('创始人用户名密码更改失败！');
		}
	}
}

class We7ResetSiteCommand extends We7Command {
	public function handle() {
		global $_W;
		$site_url = getenv('SITE_URL') ?: $this->argument('site_url');
		if (empty($site_url)) {
			$this->line('站点URL不可为空！');
			return false;
		}
		$result = ihttp_request(CLOUD_API_DOMAIN . '/site/register/profile', $site_url);
		if ($result) {
			$this->line('重置站点成功！');
		} else {
			$this->line('重置站点失败！');
		}
	}
}

class We7ModuleInstallCommand extends We7Command {
	public function handle() {
		$modulename = $this->argument('module_name');
		$moduleinfo = pdo_get('modules', array('name' => $modulename));
		if (!empty($moduleinfo)) {
			$this->line('应用已安装！');
			return true;
		}

		load()->model('extension');
		$manifest = ext_module_manifest($modulename);
		if (empty($manifest)) {
			$this->line('应用安装配置文件不存在或是格式不正确！');
			return true;
		}

		if (!empty($manifest['platform']['main_module'])) {
			$main_module_fetch = module_fetch($manifest['platform']['main_module']);
			if (empty($main_module_fetch)) {
				$this->line('请先安装主模块后再安装插件！');
				return true;
			}
			$plugin_exist = table('modules_plugin')->getPluginExists($manifest['platform']['main_module'], $manifest['application']['identifie']);
			if (empty($plugin_exist)) {
				pdo_insert('modules_plugin', array('main_module' => $manifest['platform']['main_module'], 'name' => $manifest['application']['identifie']));
			}
		}

		$check_manifest_result = ext_manifest_check($modulename, $manifest);
		if (is_error($check_manifest_result)) {
			$this->line($check_manifest_result['message']);
			return true;
		}
		$check_file_result = ext_file_check($modulename, $manifest);
		if (is_error($check_file_result)) {
			$this->line('模块缺失文件，请检查模块文件中site.php, processor.php, module.php, receiver.php 文件是否存在！');
			return true;
		}

		$module = ext_module_convert($manifest);
		if (file_exists(IA_ROOT . '/addons/' . $module['name'] . '/icon-custom.jpg')) {
			$module['logo'] = 'addons/' . $module['name'] . '/icon-custom.jpg';
		} else {
			$module['logo'] = 'addons/' . $module['name'] . '/icon.jpg';
		}

		if (!empty($manifest['platform']['plugin_list'])) {
			foreach ($manifest['platform']['plugin_list'] as $plugin) {
				pdo_insert('modules_plugin', array('main_module' => $manifest['application']['identifie'], 'name' => $plugin));
			}
		}

		$points = ext_module_bindings();
		if (!empty($points)) {
			$bindings = array_elements(array_keys($points), $module, false);
			table('modules_bindings')->deleteByName($manifest['application']['identifie']);
			foreach ($points as $name => $point) {
				unset($module[$name]);
				if (is_array($bindings[$name]) && !empty($bindings[$name])) {
					foreach ($bindings[$name] as $entry) {
						$entry['module'] = $manifest['application']['identifie'];
						$entry['entry'] = $name;
						if ('page' == $name && !empty($wxapp_support)) {
							$entry['url'] = $entry['do'];
							$entry['do'] = '';
						}
						table('modules_bindings')->fill($entry)->save();
					}
				}
			}
		}
		$module['permissions'] = iserializer($module['permissions']);
		if (!empty($manifest['application']['cloud_setting'])) {
			$module['settings'] = 2;
		}

		ext_module_run_script($manifest, 'install');
		$module['application_type'] = APPLICATION_TYPE_MODULE;
		$module['title_initial'] = get_first_pinyin($module['title']);
		$module['from'] = 'local';
		$module['status'] = STATUS_ON;

		if (pdo_insert('modules', $module)) {
			cache_build_module_subscribe_type();
			cache_build_module_info($modulename);
			cache_delete(cache_system_key('user_modules', array('uid' => 1)));
			$this->line('应用安装成功！');
			return true;
		}
		$this->line('应用安装失败！');
		return true;
	}
}

class We7ModuleUpgradeCommand extends We7Command {
	public function handle() {
		$modulename = $this->argument('module_name');
		$moduleinfo = pdo_get('modules', array('name' => $modulename));
		if (empty($moduleinfo)) {
			$this->line('应用未安装或已删除！');
			return true;
		}

		load()->model('extension');
		$manifest = ext_module_manifest($modulename);
		if (empty($manifest)) {
			$this->line('应用安装配置文件不存在或是格式不正确！');
			return true;
		}
		$check_manifest_result = ext_manifest_check($modulename, $manifest);
		if (is_error($check_manifest_result)) {
			$this->line($check_manifest_result['message']);
			return true;
		}
		$check_file_result = ext_file_check($modulename, $manifest);
		if (is_error($check_file_result)) {
			$this->line($check_file_result['message']);
			return true;
		}

		if (!empty($manifest['platform']['plugin_list'])) {
			pdo_delete('modules_plugin', array('main_module' => $manifest['application']['identifie']));
			foreach ($manifest['platform']['plugin_list'] as $plugin) {
				pdo_insert('modules_plugin', array('main_module' => $manifest['application']['identifie'], 'name' => $plugin));
			}
		}

		$module_upgrade = ext_module_convert($manifest);
		unset($module_upgrade['name'], $module_upgrade['title'], $module_upgrade['ability'], $module_upgrade['description']);

		$points = ext_module_bindings();
		$bindings = array_elements(array_keys($points), $module_upgrade, false);
		foreach ($points as $point_name => $point_info) {
			unset($module_upgrade[$point_name]);
			if (is_array($bindings[$point_name]) && !empty($bindings[$point_name])) {
				foreach ($bindings[$point_name] as $entry) {
					$entry['module'] = $manifest['application']['identifie'];
					$entry['entry'] = $point_name;
					if ('page' == $point_name && !empty($wxapp_support)) {
						$entry['url'] = $entry['do'];
						$entry['do'] = '';
					}
					$entry['title'] = empty($entry['title']) ? '' : $entry['title'];
					if ($entry['title'] && $entry['do']) {
						$not_delete_do[] = $entry['do'];
						$module_binding = table('modules_bindings')->getByEntryDo($modulename, $point_name, $entry['do']);
						if (!empty($module_binding)) {
							pdo_update('modules_bindings', $entry, array('eid' => $module_binding['eid']));
							continue;
						}
					} elseif ($entry['call']) {
						$not_delete_call[] = $entry['call'];
						$module_binding = table('modules_bindings')->getByEntryCall($modulename, $point_name, $entry['call']);
						if (!empty($module_binding)) {
							pdo_update('modules_bindings', $entry, array('eid' => $module_binding['eid']));
							continue;
						}
					}
					pdo_insert('modules_bindings', $entry);
				}
				$modules_bindings_table = table('modules_bindings');
				$modules_bindings_table
					->searchWithModuleEntry($manifest['application']['identifie'], $point_name)
					->where('call', '')
					->where('do !=', empty($not_delete_do) ? '' : $not_delete_do)
					->delete();
				$modules_bindings_table
					->searchWithModuleEntry($manifest['application']['identifie'], $point_name)
					->where('do', '')
					->where('title', '')
					->where('call !=', empty($not_delete_call) ? '' : $not_delete_call)
					->delete();
				unset($not_delete_do, $not_delete_call);
			} else {
				table('modules_bindings')->searchWithModuleEntry($manifest['application']['identifie'], $point_name)->delete();
			}
		}

		ext_module_run_script($manifest, 'upgrade');

		$module_upgrade['permissions'] = iserializer($module_upgrade['permissions']);
		if (!empty($manifest['application']['cloud_setting'])) {
			$module_upgrade['settings'] = 2;
		} else {
			$module_upgrade['settings'] = empty($module_upgrade['settings']) ? 0 : 1;
		}

		pdo_update('modules', $module_upgrade, array('name' => $modulename));

		cache_build_account_modules();
		cache_delete(cache_system_key('cloud_transtoken'));
		cache_build_module_info($modulename);
		cache_build_uni_group();
		$this->line('应用更新成功！');
		return true;
	}
}

class We7ModuleUninstallCommand extends We7Command {
	public function handle() {
		$modulename = $this->argument('module_name');
		$moduleinfo = pdo_get('modules', array('name' => $modulename));
		if (empty($moduleinfo)) {
			$this->line('应用未安装或已卸载！');
			return true;
		}
		if (!empty($moduleinfo['issystem'])) {
			$this->line('系统模块不能卸载！');
			return true;
		}

		load()->model('extension');
		$manifest = ext_module_manifest($modulename);
		if (empty($manifest)) {
			$this->line('应用安装配置文件不存在或是格式不正确！');
			return true;
		}

		if (MODULE_SUPPORT_WXAPP == $moduleinfo['wxapp_support']) {
			$wxapp_version_table = table('wxapp_versions');
			$wxapp_versions = $wxapp_version_table->where(array('modules LIKE' => "%$modulename%"))->getall();
			foreach ($wxapp_versions as $wxapp_version) {
				$modules = iunserializer($wxapp_version['modules']);
				foreach ($modules as $key => $module) {
					if ($key != $modulename) {
						continue;
					}
					unset($modules[$key]);
					break;
				}
				$wxapp_version_table->where(array('id' => $wxapp_version['id']))->fill(array('modules' => iserializer($modules)))->save();
				cache_delete(cache_system_key('miniapp_version', array('version_id' => $wxapp_version['id'])));
			}
		}
		if (APPLICATION_TYPE_TEMPLATES == $moduleinfo['application_type']) {
			pdo_delete('site_styles', array('templateid' => $moduleinfo['mid']));
			pdo_delete('site_styles_vars', array('templateid' => $moduleinfo['mid']));
		}

		ext_module_clean($modulename, true);
		ext_execute_uninstall_script($modulename);
		cache_build_module_subscribe_type();
		cache_build_account_modules(0, 1);
		cache_build_module_info($modulename);

		$this->line('应用卸载成功！');
		return true;
	}
}
